﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Media.Media3D;
using System.Windows.Media;
using System.Windows.Controls;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Markup;
using SkeletalAnimation3D.Core;
using System.Windows;
using System.Windows.Data;

namespace SkeletalAnimation3D
{

    public class Model : DependencyObject
    {
        private Animator _animator;
        private BoneAnimationLibrary _animationLibrary;

        public static Model Load(Stream stream)
        {
            BoneAnimationLibrary animationLibrary = BoneAnimationLibrary.Load(stream);
            return new Model(animationLibrary);
        }

        public Model(Point3DCollection points, Vector3DCollection normals, PointCollection textureCoordinates):
            this(new BoneAnimationLibrary(points, normals, textureCoordinates))
        {
        }

        public SkeletonInfo Skeleton
        {
            get { return (SkeletonInfo)GetValue(SkeletonProperty); }
            set { SetValue(SkeletonProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Skeleton.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SkeletonProperty =
            DependencyProperty.Register("Skeleton", typeof(SkeletonInfo), typeof(Model), new UIPropertyMetadata(null));


        public Model(BoneAnimationLibrary animationLibrary)
        {
            _animationLibrary = animationLibrary;
            MeshInterpolator interpolator = new MeshInterpolator(_animationLibrary);
            _animator = new Animator(interpolator);

            Binding binding = new Binding("Mesh");
            binding.Source = interpolator;
            binding.Mode = BindingMode.OneWay;
            BindingOperations.SetBinding(this, Model.MeshProperty, binding);
            
            binding = new Binding("Skeleton");
            binding.Source = interpolator;
            binding.Mode = BindingMode.OneWay;
            BindingOperations.SetBinding(this, Model.SkeletonProperty, binding);
        }

        public Material Material
        {
            get { return (Material)GetValue(MaterialProperty); }
            set { SetValue(MaterialProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Material.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaterialProperty =
            DependencyProperty.Register("Material", typeof(Material), typeof(Model), new UIPropertyMetadata(null));


        public MeshInfo Mesh
        {
            get { return (MeshInfo)GetValue(MeshProperty); }
            set { SetValue(MeshProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MeshPoints.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MeshProperty =
            DependencyProperty.Register("Mesh", typeof(MeshInfo), typeof(Model), new UIPropertyMetadata(null));

        public bool IsPlayingAnimation
        {
            get
            {
                return _animator.IsPlayingAnimation;
            }
        }

      
        public void ShowMesh()
        {
            _animator.ShowMesh();
        }

        public void Play()
        {
            this.Play(String.Empty);
        }

        public void Play(string animationName)
        {
            this.Play(animationName, 1);
        }
        public void Play(string animationName, int loops)
        {
            this.Play(animationName, loops, 1);
        }

        public void Play(string animationName, int loops, double speed)
        {
            this.Play(animationName, loops, speed, false);
        }

        public void Play(string animationName, int loops, double speed, bool autoReverse)
        {
            _animator.Play(_animationLibrary[animationName], loops, speed, autoReverse);
        }
        
        public void PausePlaying()
        {
            _animator.PausePlaying();
        }

        public void ContinuePlaying()
        {
            _animator.ContinuePlaying();
        }

        public void StopPlaying()
        {
            _animator.StopPlaying();
        }

        public void ShowAnimationStill(string animationName, double offset)
        {
            _animator.ShowAnimationStill(_animationLibrary[animationName], offset);
        }

        public void ShowAnimationFrame(string animationName, int frame)
        {
            _animator.ShowAnimationFrame(_animationLibrary[animationName], frame);
        }

        public void Save(Stream stream)
        {
            _animationLibrary.Save(stream);
        }

        public void SetBoneSize(int boneId, double size)
        {
            _animationLibrary.SetBoneSize(boneId, size);
        }

        public void SetBoneName(int boneId, string name)
        {
            _animationLibrary.SetBoneName(boneId, name);
        }

        public void MoveBone(int boneId, Vector3D offset)
        {
            _animationLibrary.MoveBone(boneId, offset);
        }

        public void AddBone(int parentBoneId, string name, Point3D position, double size)
        {
            _animationLibrary.AddBone(parentBoneId, name, position, size);
        }

        public Int32Collection GetAttachedPoints(int boneId)
        {
            return _animationLibrary.GetAttachedPoints(boneId);
        }

        public DoubleCollection GetPointWeights(int boneId)
        {
            return _animationLibrary.GetPointWeights(boneId);
        }

        public void AttachPointsToBone(int boneId, Int32Collection pointIds)
        {
            _animationLibrary.AttachPointsToBone(boneId, pointIds);
        }

        public void DetachPointsFromBone(int boneId, Int32Collection pointIds)
        {
            _animationLibrary.DetachPointsFromBone(boneId, pointIds);
        }

        public void SetPointWeightsOnBone(int boneId, Int32Collection pointIds, double weight)
        {
            _animationLibrary.SetPointWeightsOnBone(boneId, pointIds, weight);
        }
    }
}
